package Transaction;

import java.io.*;

import storage.data.PageID;

import Common.ByteArray;
import Metadata.HeapfileInterface;


public class Transaction {
	static final boolean txOn =true;
	static final boolean logOn = true;
	static final boolean concurrencyOn = true ;		
	static int globalTx = 0 ;
	int txnum ; 
	
	public int getTxNum () {
		return txnum ; 
	}
	
	public Transaction() {
		if ( ! txOn ) return ; 
		try {
			txnum = getNextTxNum () ;
			if ( concurrencyOn ) Concurrency.initTransaction( txnum ) ;
			if ( logOn ) start ()  ;
		}
		catch ( Exception e ) {
			System.out.println ( "Fatal Error occur when Initializing Recoery") ; 
			System.exit ( 0 ) ;
		}
	}
	
	private static synchronized int getNextTxNum () {
		return globalTx ++ ; 
	}
	
	public void start () throws Exception {
		if ( ! txOn ) return ; 
		if ( logOn ) Recovery.start(txnum) ;
	}
		
	public void commit() throws Exception {
		if ( ! txOn ) return ; 
		if ( concurrencyOn ) Concurrency.releaseAll(txnum) ;
		if ( logOn ) Recovery.commit( txnum ); 
	}

	public void rollback() throws Exception {
		if ( ! txOn ) return ; 
		if ( concurrencyOn ) Concurrency.releaseAll(txnum) ;
		if ( logOn ) Recovery.rollback(txnum) ;
	}
		
	public int getInt(String table, byte[] data, int pos) throws Exception {
		if ( concurrencyOn )
			if ( ! Concurrency.SLock( txnum, table)) throw new Exception ( "transaction:" + txnum + " error occur when get from table: " + table ) ; 
		int result = ByteArray.readInt( data , pos) ; 
		return result ;
	}
	
	public String getString(String table, byte[] data, int pos ) throws Exception {
		if ( concurrencyOn )
			if ( ! Concurrency.SLock(txnum, table)) throw new Exception ( "transaction:" + txnum + " rror occur when get from table: " + table ) ; 
		String result = ByteArray.readString( data , pos ) ; 
		return result ;
	}
	
	public char getChar (String table,  byte[] data, int pos ) throws Exception {
		if ( concurrencyOn )
			if ( ! Concurrency.SLock(txnum, table)) throw new Exception ( "transaction:" + txnum +  " error occur when get from table: " + table ) ; 
		char result = ByteArray.readChar( data, pos);
		return result ;
	}

	public float getFloat (String table,  byte[] data, int pos ) throws Exception {
		if ( concurrencyOn )
			if ( ! Concurrency.SLock(txnum, table)) throw new Exception ( "transaction:" + txnum +  " error occur when get from table: " + table ) ; 
		float result = ByteArray.readFloat( data  , pos) ; 
		return result ;
	}

	public short getShort (String table,  byte[] data, int pos ) throws Exception {
		if ( concurrencyOn )
			if ( ! Concurrency.SLock(txnum, table)) throw new Exception ( "transaction:" + txnum + " error occur when get from table: " + table ) ; 
		short result = ByteArray.readShort( data ,  pos) ;
		return result ;
	}

	public void getBytes(String table, byte [] source, int pos1, byte[] dest, int pos2, int len ) throws Exception {
		if ( concurrencyOn )
			if ( ! Concurrency.SLock(txnum, table)) throw new Exception ( "transaction:" + txnum + " error occur when get from table: " + table ) ;
		System.arraycopy(source, pos1, dest, pos2, len);
	}
	
	public void set (String table, PageID pid, byte[] data, int pos, Object val) throws Exception {
		this.set(table, pid, data, pos, val, false ) ; 
	}
		
	public void set (String table, PageID pid, byte[] data, int pos, Object val, boolean isKernel) throws Exception {
		if ( concurrencyOn && ! isKernel ){
			if ( ! Concurrency.XLock(txnum, table)) throw new Exception ( "transaction:" + txnum + " error occur when set table: " + table ) ;
		}
		if ( val instanceof Integer ){
			if ( logOn ) { 
				int old = ByteArray.readInt( data , pos ) ;
				Recovery.setInt( txnum, pid.pageId, pos, (Integer)old, (Integer)val ) ;
			}
			ByteArray.writeInt( data ,  pos , (Integer)val ) ;		
		} else if ( val instanceof String ){
			if ( logOn ) { 				
				String old = ByteArray.readString( data , pos ) ;
				Recovery.setString( txnum, pid.pageId, pos, (String)old, (String)val ) ;
			}
			ByteArray.writeString( data ,  pos , (String)val ) ;		
		} else if ( val instanceof Short ){
			if ( logOn ) {
				Short old = ByteArray.readShort( data , pos ) ;
				Recovery.setShort( txnum, pid.pageId, pos, (Short)old, (Short)val ) ;
			}
			ByteArray.writeShort( data ,  pos , (Short)val ) ;		
		} else if ( val instanceof Character ){
			if ( logOn ) {
				Character old = ByteArray.readChar( data , pos ) ;
				Recovery.setChar( txnum, pid.pageId, pos, (Character)old, (Character)val ) ;
			}
			ByteArray.writeChar( data ,  pos , (Character)val ) ;		
		} else if ( val instanceof Float ){
			if ( logOn ) {
				Float old = ByteArray.readFloat( data , pos ) ;
				Recovery.setFloat( txnum, pid.pageId, pos, (Float)old, (Float)val ) ;
			}
			ByteArray.writeFloat( data ,  pos , (Float)val ) ;		
		} 
	}
	
	public void setBytes(String table, PageID pid, byte [] source, int pos1, byte[] dest, int pos2, int len ) throws Exception {
		if ( concurrencyOn )
			if ( ! Concurrency.XLock(txnum, table)) throw new Exception ( "transaction:" + txnum + " error occur when set table: " + table ) ;
		if ( logOn ) {
			byte [] old = new byte [ len ] ;
			byte [] cur = new byte [ len ] ;
			System.out.println(dest.length);
			System.arraycopy(dest, pos2, old, 0, len) ;
			System.arraycopy(source, pos1, cur, 0, len) ;
			Recovery.setBytes(txnum, pid.pageId , pos2 , old, cur) ; 
		}
		System.arraycopy(source, pos1, dest, pos2, len);
	}

}